/*
XPTracker - Alternative Agile Tracking Tool
Copyright (C) 2006-2009  Stephen Starkey

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/
package org.xptracker.services;

import java.io.*;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class ZipUpdateProvider implements UpdateProvider {
  private static final Pattern SETUP_PATTERN = Pattern.compile("org/xptracker/autoupdate/js/setup/(.*?\\.js)");
  private static final Pattern UPDATE_PATTERN = Pattern.compile("org/xptracker/autoupdate/js/prod/(.*?\\.js)");

  @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
  private final Map<String, String> SETUP_FILE_CACHE = new TreeMap<String, String>() {
    {
      collectData(SETUP_PATTERN, this);
    }
  };

  @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
  private final Map<String, String> UPDATE_FILE_CACHE = new TreeMap<String, String>() {
    {
      collectData(UPDATE_PATTERN, this);
    }
  };

  public void collectData(final Pattern pattern, final Map<String, String> map) {
    try {
      collect(pattern, new Collector() {
        public void collect(ZipInputStream is, String name, ZipEntry entry) throws IOException {
          final ByteArrayOutputStream bout = new ByteArrayOutputStream();
          PrintStream out = new PrintStream(bout);
          dump(new InputStreamReader(is), out);
          map.put(name, new String(bout.toByteArray()));
        }
      });
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  public Iterable<? extends String> listSetupFiles() throws IOException {
    return SETUP_FILE_CACHE.keySet();
  }

  private void collect(final Pattern pattern, final Collector collector) throws IOException {
    withZip(new ZipReceiver<Object>() {
      public Object receive(ZipInputStream is) throws IOException {
        try {
          ZipEntry entry;
          while ((entry = is.getNextEntry()) != null) {
            final String name = entry.getName();
            final Matcher matcher = pattern.matcher(name);
            if (matcher.matches()) {
              collector.collect(is, matcher.group(1), entry);
            }
          }
        } finally {
          is.closeEntry();
        }
        return null;
      }
    });
  }

  private <T> T withZip(ZipReceiver<T> zipReceiver) throws IOException {
    ZipInputStream is = loadZipInputStream();
    try {
      return zipReceiver.receive(is);
    } finally {
      is.close();
    }
  }

  protected ZipInputStream loadZipInputStream() throws FileNotFoundException {
    return new ZipInputStream(getClass().getClassLoader().getResourceAsStream("updates.zip"));
  }

  public Reader loadSetupFile(String filename) throws IOException {
    return new StringReader(SETUP_FILE_CACHE.get(filename));
  }

  public Iterable<? extends String> listUpdateFiles() throws IOException {
    return UPDATE_FILE_CACHE.keySet();
  }

  public Reader loadUpdateFile(String filename) throws FileNotFoundException {
    return new StringReader(UPDATE_FILE_CACHE.get(filename));
  }

  private static interface ZipReceiver<T> {
    T receive(ZipInputStream is) throws IOException;
  }

  private static interface Collector {
    void collect(ZipInputStream is, String name, ZipEntry entry) throws IOException;
  }

  private static void dump(Reader reader, PrintStream out) throws IOException {
    char[] tmp = new char[1024];
    int bytesRead;
    while ((bytesRead = reader.read(tmp)) >= 0) {
      char[] chars = new char[bytesRead];
      System.arraycopy(tmp, 0, chars, 0, bytesRead);
      out.print(chars);
    }
  }
}
